﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using Brain;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Brain.Windows
{
    public partial class ModelControl : GraphicsDeviceControl
    {
        /// <summary>
        /// Timer used for updating and drawing the Engine
        /// </summary>
        public System.Windows.Forms.Timer Timer { get; set; }

        public Stopwatch ElapsedTime { get; set; }
        public Stopwatch TotalTime { get; set; }

        public GameTime GameTime { get; set; }

        public event SetVoid OnDraw;

        public InputFreeCamera3D Camera;
        public WorldObject WorldObject;

        public ModelControl()
        {
            ElapsedTime = new Stopwatch();
            TotalTime = new Stopwatch();

            TotalTime.Start();
        }

        public SetVoid OnStart;

        protected override void Initialize()
        {
            Timer = new System.Windows.Forms.Timer();
            Timer.Interval = (int)((1f / 60f) * 1000);
            Timer.Tick += new EventHandler(Timer_Tick);

            Camera = new InputFreeCamera3D();
            Camera.FarPlane = 10000;
            Camera.Position = new Vector3(18, 10, 10);
            Camera.DegreesRotation = new Vector3(30, -30, 0);
            Camera.InitialPosition = Camera.Position;
            Camera.InitialRotationRadians = Camera.RotationRadians;
            Camera.UpdateIfMouseOutOfScreen = true;

            Mouse.WindowHandle = Handle;

            base.Initialize();

            Timer.Start();

            if (OnStart != null)
            {
                OnStart();
            }
        }

        protected void Timer_Tick(object sender, EventArgs e)
        {
            // Invalidate everything so the whole control refreshes
            this.Invalidate();

            // Force the view update
            this.Update();
        }

        protected override void Draw()
        {
            UpdateGameTime();
            Engine.Instance.GraphicsDevice.Clear(Color.CornflowerBlue);
            if (OnDraw != null)
            {
                OnDraw();
            }
            Camera.Draw();
            if (this.WorldObject != null)
            {
                this.WorldObject.Update();
                this.WorldObject.Draw();
                Model model = this.WorldObject.Model as Model;
                if (model != null)
                {
                    GraphicsDevice device = Engine.Instance.GraphicsDevice;
                    foreach (ModelMesh modelMesh in model.Meshes)
                    {
                        foreach (ModelMeshPart mesh in modelMesh.MeshParts)
                        {
                            if (mesh.Effect == null)
                            {
                                continue;
                            }

                            if (mesh.Effect is BasicEffect)
                            {
                                BasicEffect fx = mesh.Effect as BasicEffect;
                                fx.World = Matrix.Identity;
                                fx.View = Camera.View;
                                fx.Projection = Camera.Projection;
                                fx.EnableDefaultLighting();
                                fx.PreferPerPixelLighting = true;
                            }

                            device.Indices = mesh.IndexBuffer;
                            device.SetVertexBuffer(mesh.VertexBuffer);

                            int count = mesh.Effect.CurrentTechnique.Passes.Count;

                            for (int p = 0; p < count; p++)
                            {
                                mesh.Effect.CurrentTechnique.Passes[p].Apply();

                                if (mesh.IndexBuffer == null)
                                    device.DrawPrimitives(PrimitiveType.TriangleList, 0, mesh.PrimitiveCount);
                                else
                                    device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                                        mesh.NumVertices, mesh.VertexOffset, mesh.PrimitiveCount);
                            }
                        }
                    }
                }
            }
        }

        protected void UpdateGameTime()
        {
            // Recreate the GameTime with the current values
            GameTime = new GameTime(TotalTime.Elapsed, ElapsedTime.Elapsed);

            ElapsedTime.Reset();
            ElapsedTime.Start();
        }
    }
}
